package com.mazaiting.rtda.heap;

import cn.hutool.core.util.ObjectUtil;

import java.util.Objects;

/**
 * 方法描述解析
 *
 * @author mazaiting
 * @since 2021/8/24
 */
public class MethodDescriptorParser {
    /**
     * 描述源字符串
     */
    private String raw;
    /**
     * 偏移量
     */
    private int offset;
    /**
     * 方法描述
     */
    private MethodDescriptor methodDescriptor;

    public MethodDescriptorParser(String descriptor) {
        parse(descriptor);
    }

    public MethodDescriptor getMethodDescriptor() {
        return methodDescriptor;
    }

    /**
     * 解析
     *
     * @param descriptor 描述
     */
    private void parse(String descriptor) {
        this.raw = descriptor;
        this.methodDescriptor = new MethodDescriptor();
        startParams();
        parseParamTypes();
        endParams();
        parseReturnType();
        finish();
    }

    /**
     * 开始参数
     */
    private void startParams() {
        if (readUint8() != '(') {
            causePanic();
        }
    }

    /**
     * 参数类型
     */
    private void parseParamTypes() {
        for (String tmp = parseFieldType(); ObjectUtil.isNotEmpty(tmp); tmp = parseFieldType()) {
            methodDescriptor.addParameterType(tmp);
        }
    }

    /**
     * 结束参数
     */
    private void endParams() {
        if (readUint8() != ')') {
            causePanic();
        }
    }

    /**
     * 返回类型
     */
    private void parseReturnType() {
        if (readUint8() == 'V') {
            this.methodDescriptor.setReturnType("V");
            return;
        }
        unreadUint8();
        String type = parseFieldType();
        if (Objects.nonNull(type)) {
            this.methodDescriptor.setReturnType(type);
            return;
        }
        causePanic();
    }

    /**
     * 解析完成
     */
    private void finish() {
        if (offset != raw.length()) {
            causePanic();
        }
    }

    /**
     * 抛异常
     */
    public void causePanic() {
        throw new RuntimeException("BAD descriptor: " + raw);
    }

    /**
     * 读取 8 位字符
     *
     * @return ASCII 码
     */
    private int readUint8() {
        char tmp = raw.charAt(offset);
        this.offset++;
        return tmp;
    }

    /**
     * 左移 8 位
     */
    private void unreadUint8() {
        this.offset--;
    }

    /**
     * 解析字段类型
     * @return 字符串
     */
    private String parseFieldType() {
        char tmp = (char)readUint8();
        switch (tmp) {
            case 'B':
            case 'C':
            case 'D':
            case 'F':
            case 'I':
            case 'J':
            case 'S':
            case 'Z':
                return String.valueOf(tmp);
            case 'L':
                return parseObjectType();
            case '[':
                return parseArrayType();
            default:
                unreadUint8();
                return "";
        }
    }

    /**
     * 解析数组类型
     * @return 字符串
     */
    private String parseArrayType() {
        int arrStart = offset - 1;
        parseFieldType();
        int arrEnd = offset;
        return raw.substring(arrStart, arrEnd);
    }

    /**
     * 解析对象类型
     * @return 字符串
     */
    private String parseObjectType() {
        String unread = raw.substring(offset);
        // 第一次分号出现的位置
        int semicolonIndex = unread.indexOf(';');
        if (semicolonIndex == -1) {
            causePanic();
            return "";
        } else {
            int objStart = offset - 1;
            int objEnd = offset + semicolonIndex + 1;
            this.offset = objEnd;
            return raw.substring(objStart, objEnd);
        }
    }
}


















